package cn.sell.comm.base;


import cn.sell.comm.base.mapper.MyBaseMapper;
import cn.sell.comm.base.service.MyBaseService;
import cn.sell.comm.model.po.PojoPO;
import cn.sell.comm.model.qo.PageQO;
import cn.sell.comm.model.vo.PageVO;
import cn.sell.comm.util.DateUtil;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import tk.mybatis.mapper.entity.Condition;
import tk.mybatis.mapper.entity.Example;

import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author Limi
 * @desc MYSQL通用CRUD服务类
 * 备注：使用该类时，注入泛型 E，一定要有对应的 EMapper，
 * 例如：使用User的基础服务实现类需要继承MySqlCrudServiceImpl<User, String>，
 * 前提是要有UserMapper extends myBaseMapper 类
 * @since 10/18/2017 18:31 PM
 */
@Log4j2
@Transactional
public abstract class MySqlCrudServiceImpl<E extends PojoPO<PK>, PK> implements MyBaseService<E, PK> {

    @Autowired
    protected MyBaseMapper<E> myBaseMapper;

    protected Class<E> poType;

    public MySqlCrudServiceImpl() {
        ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
        poType = (Class<E>) pt.getActualTypeArguments()[0];
    }

    @Override
    public PK insert(E record) {
        Assert.notNull(record, "record is not null；[实体类不能为空]");

        if (record.getCreateTime() == null) {
            record.setCreateTime(DateUtil.getDateTimeStr(new Date()));
            record.setVersion("1");
        }
        myBaseMapper.insertSelective(record);
        log.info("record:", record.toString());
        return record.getId();
    }

    @Override
    public int deleteByPk(PK pk) {
        Assert.notNull(pk, "pk is not null；[主键不能为空]");
        return myBaseMapper.deleteByPrimaryKey(pk);
    }

    @Override
    public int deleteByPks(Iterable<PK> pks) {
        Assert.notNull(pks, "pks is not null");
        String pksStr = this.IterableToSpitStr(pks, ",");
        if (pksStr == null) {
            return 0;
        }
        return myBaseMapper.deleteByIds(pksStr);
    }

    @Override
    public int updateByPk(PK pk, E record) {
        Assert.notNull(pk, "pk is not null；[主键不能为空]");
        Assert.notNull(record, "record is not null；[实体类不能为空]");

        record.setId(pk);
        record.setUpdateTime(DateUtil.getDateTimeStr(new Date()));
        String version = record.getVersion();
        int i = 1;
        log.info(version);
        if (null != version && "".equals(version)) {
            i = Integer.valueOf(version);
        }
        record.setVersion(String.valueOf(i += 1));
        return myBaseMapper.updateByPrimaryKey(record);
    }

    @Override
    public int updateByPkSelective(PK pk, E record) {
        Assert.notNull(pk, "pk is not null；[主键不能为空]");
        Assert.notNull(record, "record is not null；[实体类不能为空]");

        record.setId(pk);
        if (record.getUpdateTime() == null) {
            record.setUpdateTime(DateUtil.getDateTimeStr(new Date()));
            String version = record.getVersion();
            int i = 1;
            log.info(version);
            if (null != version && "".equals(version)) {
                i = Integer.valueOf(version);
            }
            record.setVersion(String.valueOf(i += 1));
        }
        return myBaseMapper.updateByPrimaryKeySelective(record);
    }

    @Override
    public PK saveOrUpdate(E record) {
        Assert.notNull(record, "record is not null；[实体类不能为空]");
        E e = selectByPk(record.getId());
        if (null != record.getId() && null != e) {
            record.setVersion(e.getVersion());
            record.setCreateTime(e.getCreateTime());
            record.setCreateUser(e.getCreateUser());
            updateByPk(record.getId(), record);
        } else {
            insert(record);
        }
        return record.getId();
    }

    @Override
    public E selectByPk(PK pk) {
        Assert.notNull(pk, "pk is not null；[主键不能为空]");
        return myBaseMapper.selectByPrimaryKey(pk);
    }

    @Override
    public List<E> selectByPks(Iterable<PK> pks) {
        Assert.notNull(pks, "pks is not null");

        String pksStr = this.IterableToSpitStr(pks, ",");
        if (pksStr == null) {
            return new ArrayList<>();
        }
        return myBaseMapper.selectByIds(pksStr);
    }

    private String IterableToSpitStr(Iterable<PK> pks, String separator) {
        StringBuilder s = new StringBuilder();
        pks.forEach(pk -> s.append(pk).append(separator));

        if (StringUtils.isEmpty(s.toString())) {
            return null;
        } else {
            s.deleteCharAt(s.length() - 1);
        }

        return s.toString();
    }

    @Override
    public List<E> selectAll() {
        List<E> es = myBaseMapper.selectAll();
        return es;
    }

    @Override
    public List<E> selectByEntity(E condition) {
        return myBaseMapper.select(condition);
    }

    @Override
    public List<E> selectByExample(Object example) {
        return myBaseMapper.selectByExample(example);
    }

    @Override
    public List<E> selectByCondition(Condition condition) {
        return myBaseMapper.selectByCondition(condition);
    }

    @Override
    public PageVO<E> selectPage(PageQO<?> pageQO) {
        Assert.notNull(pageQO, "pageQO is not null");

        Page<E> page = PageHelper.startPage(pageQO.getPageNum(), pageQO.getPageSize(), pageQO.getOrderBy());
        try {
            Object condition = pageQO.getCondition();

            if (condition == null) {
                myBaseMapper.selectAll();
            } else if (condition instanceof Condition) {
                myBaseMapper.selectByCondition(condition);
            } else if (condition instanceof Example) {
                myBaseMapper.selectByExample(condition);
            } else if (poType.isInstance(condition)) {
                myBaseMapper.select((E) condition);
            } else {
                try {
                    E e = poType.newInstance();
                    BeanUtils.copyProperties(condition, e);
                    myBaseMapper.select(e);
                } catch (InstantiationException | IllegalAccessException e) {
                    log.error("selectPage occurs error, caused by: ", e);
                    throw new RuntimeException("poType.newInstance occurs InstantiationException or IllegalAccessException", e);
                }
            }
        } finally {
            page.close();
        }
        return PageVO.build(page);
    }

}


